package xyz.heyaoshare.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

/**
 * SQL注入拦截器
 * @author YueHe
 * @version 0.0.1
 * @since 2025/6/26 下午4:43
 */
@Slf4j
@Component
public class SQLInjectionInterceptor implements HandlerInterceptor {

    private static final List<String> SQL_KEYWORDS = Arrays.asList("\"", "'", "--", ";", "/*", "*/", "xp_", "sp_", "union", "select", "insert", "delete", "update", "exec", "execute", "alter", "drop", "create", "truncate", "comment", "declare", "merge", "call", "replace");

    /**
     * 判断输入的字符串是否为SQL安全字符串。
     *
     * @param input 要判断的字符串
     * @return 如果输入字符串为null或空字符串，或者不包含任何SQL关键字，则返回true，否则返回false
     */
    public boolean isSQLSafe(String input) {
        if (input == null || input.isEmpty()) {
            return Boolean.TRUE;
        }
        String lowerInput = input.toLowerCase();
        return SQL_KEYWORDS.stream().noneMatch(lowerInput::contains);
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod handlerMethod) {
            // 获取方法参数
            Class<?>[] parameterTypes = handlerMethod.getMethod().getParameterTypes();
            // 获取请求参数
            Enumeration<String> paramNames = request.getParameterNames();
            final String uri = request.getRequestURI();
            while (paramNames.hasMoreElements()) {
                String paramName = paramNames.nextElement();
                String paramValue = request.getParameter(paramName);
                // 检查每个字符串类型的参数
                if (String.class.equals(parameterTypes[paramName.hashCode() % parameterTypes.length]) && !isSQLSafe(paramValue)) {
                    log.error("请求参数包含SQL注入风险: URL:{}【{}={}】", uri, paramName, paramValue);
                    response.sendError(400, "请求参数包含SQL注入风险");
                    return Boolean.FALSE;
                }
            }
        }
        return Boolean.TRUE;
    }

}